/*
    日志器模块
        1、抽象日志器基类
        2、派生出不同的子类 （同步日志器类 & 异步日志器类）      
*/
#ifndef __CLX_LOGGER_H__
#define __CLX_LOGGER_H__

#include "util.hpp"
#include "sink.hpp"
#include "level.hpp"
#include "format.hpp"
#include "looper.hpp"

#include <mutex>
#include <thread>
#include <atomic>
#include <cstdarg>
#include <unordered_map>


namespace clxlog{


    class Logger {
    public:
        using ptr = std::shared_ptr<Logger>;
        Logger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
            :_logger_name(logger_name), _limit_level(level), _formatter(formatter), _sinks(sinks.begin(), sinks.end()) {}

        const std::string &name() { return _logger_name; }
        /* 构造日志消息对象过程， 并得到格式化后的日志消息字符串-- 然后进行落地输出*/
        void debug(const std::string& file, size_t line, const std::string &fmt, ...) {
            /* 通过传入的参数构造出一个日志消息对象，进行日志格式化，最终落地*/
            // 1、 判断当前日志等级是否达到输出标准
            if (LogLevel::value::DEBUG < _limit_level) { return ;}
            // 2、 对fmt格式化字符串和不定参进行字符串组织，得到日志消息的字符串
            va_list ap;
            va_start(ap, fmt);
            char *res = nullptr;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) { std::cout << "vasprintf failed! " << std::endl; return;}
            va_end(ap);
            // 3、 构造LogMsg对象
            LogMsg msg(LogLevel::value::DEBUG, line, file, _logger_name, res);
            // 4、 通过格式化工具对LogMsg进行格式化，获得格式化后的日志字符串
            std::stringstream ss;
            _formatter->format(ss, msg);
            // 5、 进行日志落地
            log(ss.str().c_str(), ss.str().size());
            free(res);   // vasprintf() 内部开辟空间了，是动态申请的，需要我们手动释放
        }
        void info (const std::string& file, size_t line, const std::string &fmt, ...) {
            if (LogLevel::value::INFO < _limit_level) { return ;}
            va_list ap;
            va_start(ap, fmt);
            char *res = nullptr;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) { std::cout << "vasprintf failed! " << std::endl; return;}
            va_end(ap);
            LogMsg msg(LogLevel::value::INFO, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);

        }
        void warn (const std::string& file, size_t line, const std::string &fmt, ...){
            if (LogLevel::value::WARN < _limit_level) { return ;}
            va_list ap;
            va_start(ap, fmt);
            char *res = nullptr;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) { std::cout << "vasprintf failed! " << std::endl; return;}
            va_end(ap);
            LogMsg msg(LogLevel::value::WARN, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);   
            
        }
        void error(const std::string& file, size_t line, const std::string &fmt, ...){
            if (LogLevel::value::ERROR < _limit_level) { return ;}
            va_list ap;
            va_start(ap, fmt);
            char *res = nullptr;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) { std::cout << "vasprintf failed! " << std::endl; return;}
            va_end(ap);
            LogMsg msg(LogLevel::value::ERROR, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);   
            
        }
        void fatal(const std::string& file, size_t line, const std::string &fmt, ...){
            if (LogLevel::value::FATAL < _limit_level) { return ;}
            va_list ap;
            va_start(ap, fmt);
            char *res = nullptr;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1) { std::cout << "vasprintf failed! " << std::endl; return;}
            va_end(ap);
            LogMsg msg(LogLevel::value::FATAL, line, file, _logger_name, res);
            std::stringstream ss;
            _formatter->format(ss, msg);
            log(ss.str().c_str(), ss.str().size());
            free(res);  
        }
    protected:
        /* 抽象接口完成实际的落地输出 -- 不同的日志器会有不同的实际落地方式 */
        virtual void log(const char* data, size_t len) = 0;
    protected:
        std::mutex _mutex;
        std::string _logger_name;
        std::atomic<LogLevel::value> _limit_level;    // 需要频繁访问
        Formatter::ptr _formatter;
        std::vector<LogSink::ptr> _sinks;
    };

    class SynchLogger : public Logger {
        public:
            SynchLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, std::vector<LogSink::ptr> &sinks)
                : Logger(logger_name, level, formatter, sinks){}
        protected:
            /* 同步日志器，是将日志直接通过落地模块 句柄进行日志落地 */
            void log(const char *data, size_t len) override{
                std::unique_lock<std::mutex> lock(_mutex);
                if (_sinks.empty()) return;
                for (auto &sink : _sinks) {
                    sink->log(data, len);
                }
            }
    };

    class AsynchLogger : public Logger {
        public:
            AsynchLogger(const std::string &logger_name, LogLevel::value level, Formatter::ptr &formatter, 
            std::vector<LogSink::ptr> &sinks, AsynchType lopper_type)
                : Logger(logger_name, level, formatter, sinks), 
                _looper(std::make_shared<AsynchLooper>(std::bind(&AsynchLogger::realLog, this, std::placeholders::_1), lopper_type)){}
            /* 将数据写入缓冲区*/
            void log(const char *data, size_t len) override{
                _looper->push(data, len);
            }
            /* 设计一个实际落地函数（将缓冲区中的数据落地） */
            void realLog(Buffer &buf) {
                if (_sinks.empty()) return;
                for (auto &sink : _sinks) {
                    sink->log(buf.begin(), buf.readAbleSize());
                }
            }
        private: 
            AsynchLooper::ptr _looper;
    };

    /* 
        使用建造者模式来构造日志器， 不让用户去直接构造日志器， 简化使用复杂度
        1、抽象出一个日志建造者类 (完成日志器对象所需零部件的构建 & 日志器构建)
            (一) 设置日志器类型
            (二) 将不同类型的日志器的创建放到同一个日志器建造者类中完成
        2、派生出具体的建造者类 -- 局部日志器建造者 & 全局日志器建造者 (后边添加了全局单例管理器之后，将日志器添加全局管理)
    */

    enum class LoggerType {
        LOGGER_SYNCH,
        LOGGER_ASYNCH
    };

    class LoggerBuilder {
        public:
            using ptr = std::shared_ptr<LoggerBuilder>;
            LoggerBuilder() : _logger_type(LoggerType::LOGGER_SYNCH), _limit_level(LogLevel::value::DEBUG), _looper_type(AsynchType::ASYNC_SAFE){}
            void buildLoggerType(LoggerType type)                 { _logger_type = type; }
            void buildLoggerName(const std::string &name)         { _logger_name = name; }
            void buildLoggerLevel(LogLevel::value level)          { _limit_level = level;  }
            void buildLoggerFormatter(const std::string &pattern) {  _formatter.reset(new Formatter(pattern)); }
            void buildEnableUnSafeAsynch() { _looper_type = AsynchType::ASYNC_UNSAFE; } 
            template<typename SinkType, typename ...Args>
            void buildSink(Args&&... args) { _sinks.push_back(SinkFactory::create<SinkType>(std::forward<Args>(args)...)); }
            virtual Logger::ptr build() = 0; //  建造日志器
        protected:
            AsynchType       _looper_type;
            LoggerType       _logger_type;
            std::string      _logger_name;
            LogLevel::value  _limit_level;    // 需要频繁访问
            Formatter::ptr   _formatter;
            std::vector<LogSink::ptr>  _sinks;
   };

    class LocalLoggerBuilder : public LoggerBuilder {
        public:
            virtual Logger::ptr build() override {
                assert(!_logger_name.empty());      // 必须有日志器名称
                if (_formatter.get() == nullptr) { _formatter = std::make_shared<Formatter>(); }
                if (_sinks.empty()) { buildSink<StdoutSink>(); }
                if (_logger_type == LoggerType::LOGGER_ASYNCH) {
                    return std::make_shared<AsynchLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
                }
                return std::make_shared<SynchLogger>(_logger_name, _limit_level, _formatter, _sinks);
            }
    };

    class LoggerManager {
        public:
            static LoggerManager& getInstance() {
                /* C++11针对静态局部变量，在编译层面实现了线程安全，在静态局部变量没有构造完成之前，其他线程进入就会阻塞 */
                static LoggerManager eton;
                return eton;
            }
            void addLogger(Logger::ptr &logger) {
                if (hasLogger(logger->name())) { return ;}
                std::unique_lock<std::mutex> lock(_mutex);
                _loggers.insert(make_pair(logger->name(), logger));
            }
            bool hasLogger(const std::string &name) {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if (it == _loggers.end()) {
                    return false;
                }
                return true;
            }
            Logger::ptr getLogger(const std::string &name) {
                std::unique_lock<std::mutex> lock(_mutex);
                auto it = _loggers.find(name);
                if (it == _loggers.end()) {
                    return Logger::ptr();
                }
                return it->second;
            }
            Logger::ptr rootLogger() {
                return _root_logger;
            }

        private:
            LoggerManager() {
                std::unique_ptr<clxlog::LoggerBuilder> builder(new clxlog::LocalLoggerBuilder());
                builder->buildLoggerName("root");
                _root_logger = builder->build();
                _loggers.insert(std::make_pair("root", _root_logger));
            }
        private:
            std::mutex _mutex;
            Logger::ptr _root_logger;          // 默认日志器
            std::unordered_map<std::string, Logger::ptr> _loggers;
    };

    /* 全局日志器建造者 -- 在局部的基础上新增：自动添加日志器到单例对象中 */
    class GlobalLoggerBuilder : public LoggerBuilder {
        public:
            virtual Logger::ptr build() override {
                assert(!_logger_name.empty());      // 必须有日志器名称
                if (_formatter.get() == nullptr) { _formatter = std::make_shared<Formatter>(); }
                if (_sinks.empty()) { buildSink<StdoutSink>(); }
                Logger::ptr logger;
                if (_logger_type == LoggerType::LOGGER_ASYNCH) {
                    logger = std::make_shared<AsynchLogger>(_logger_name, _limit_level, _formatter, _sinks, _looper_type);
                } else {
                    logger = std::make_shared<SynchLogger>(_logger_name, _limit_level, _formatter, _sinks);
                }
                LoggerManager::getInstance().addLogger(logger);
                return logger;
            }
    };

}
#endif